﻿/************ CHECK COLUMN EXTENDED TYPE *****************/
Ext.grid.CheckColumn = function(config) {
    Ext.apply(this, config);
    if (!this.id) {
        this.id = Ext.id();
    }
    this.renderer = this.renderer.createDelegate(this);
};

Ext.grid.CheckColumn.prototype = {
    init: function(grid) {
        this.grid = grid;
        this.grid.on('render', function() {
            var view = this.grid.getView();
            view.mainBody.on('mousedown', this.onMouseDown, this);
        }, this);
    },

    onMouseDown: function(e, t) {
        if (t.className && t.className.indexOf('x-grid3-cc-' + this.id) != -1) {
            e.stopEvent();
            var index = this.grid.getView().findRowIndex(t);
            var record = this.grid.store.getAt(index);
            record.set(this.dataIndex, !record.data[this.dataIndex]);
        }
    },

    renderer: function(v, p, record) {
        p.css += ' x-grid3-check-col-td';
        return '<div class="x-grid3-check-col' + (v ? '-on' : '') + ' x-grid3-cc-' + this.id + '">&#160;</div>';
    }
};

var ctxMenuRecord;

/**
* From ExtJS Forum post http://extjs.com/forum/showthread.php?t=20859
* Updates the values passed with a data object using a configured convert() function.
* @param {Object} Raw data to update the record instance with.
*/
Ext.data.Record.prototype.convert = function(data) {
    var fs = this.fields, fi = fs.items, fl = fs.length, f, map;
    for (var j = 0; j < fl; j++) {
        f = fi[j];
        map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
        if (data[map] !== undefined) {
            this.set(f.name, f.convert(data[map]));
        }
    }
}

/**
* Set the named field to the specified value using a configured convert() function.
* @param {String} name The name of the field to set.
* @param {Object} value The raw value to set the field to.
*/
Ext.data.Record.prototype.convertValue = function(key, value) {
    var fs = this.fields, fi = fs.items, fl = fs.length, f, map;
    for (var j = 0; j < fl; j++) {
        f = fi[j];
        map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
        if (key === map) {
            this.set(f.name, f.convert(value));
            break;
        }
    }
}

/*** From Animal's suggestion on http://extjs.com/forum/showthread.php?t=13044 **/
Ext.override(Ext.data.Record, {
    newRecord: function(newId) {
        var newData = {};
        for (var i = 0; i < this.fields.getCount(); i++) {
            newData[this.fields.item(i).name] = '';
        }
        return new this.constructor(newData, newId || ++Ext.data.Record.AUTO_ID);
    }
});



/**
* Set the named field to the specified value using a configured convert() function.
* @param {String} name The name of the field to set.
* @param {Object} value The raw value to set the field to.
*/
Ext.data.Record.prototype.loadDefaults = function() {
    var fs = this.fields, fi = fs.items, fl = fs.length, f, map;
    this.data = {};
    for (var j = 0; j < fl; j++) {
        f = fi[j];
        map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
        this.data[map] = f.convert('');
    }
}

GRID_DEFAULT_PAGE_SIZE = 10;

//************** STATES DS ********************
var dsStates = new Ext.data.SimpleStore({
    fields: ['abbr', 'state', 'nick'],
    data: Ext.exampledata.states // from states.js
});

//************** RECORD LAYOUT ***************
var userFieldDefs = [
    'UserID',
    'UserName',
    'FirstName',
    'LastName',
    'Password',
    'Email',
    'Address1',
    'Address2',
    'City',
    'State',
    'Zip',
    'HomePhone',
    'MobilePhone',
    { name: 'PasswordSetDate', type: 'date', dateFormat: 'n/j/Y h:i:s A' },
    { name: 'PasswordChangeRequired', type: 'boolean' }
];

var userRecordDef = Ext.data.Record.create(userFieldDefs);

//************** READER ********************
var myReader = new Ext.data.JsonReader({
    root: 'data',
    totalProperty: 'totalcount',
    successProperty: 'success',
    id: 'UserID'
},
    userRecordDef
);

//************** USERS DS ********************
var dsUsers = new Ext.data.Store({
    url: 'rest/user/',
    remoteSort: true,
    restful: true,
    /*method: 'GET',*/
    stripeRows: true,
    reader: myReader
});
/*dsUsers.proxy.conn.method = "GET";*/

//*********** AJAX EXCEPTION HANDLING ****************
dsUsers.on('loadexception', function(event, options, response, error) {
    if (response.status == 200) {
        var json = Ext.decode(response.responseText);
    } else {
        alert("Recieved HTML status=" + response.status + " when trying to call " + event.url);
    }
    mask.hide();
    SetStatus("Problem loading data.", false);
    self.error_occured = true;
    prevent_on_load_callback_function();
});


//************** GRID PAGING BAR ********************
var pagingBar = new Ext.PagingToolbar({
    pageSize: GRID_DEFAULT_PAGE_SIZE,
    store: dsUsers,
    url: 'rest/user/',
    method: 'GET',
    displayInfo: true,
    displayMsg: 'Displaying records {0} - {1} of {2}',
    emptyMsg: "No records to display",

    items: [
        '-', 'Items per page:', '-',
        {
            xtype: 'combo', triggerAction: 'all', width: 50, mode: 'local',
            store: new Ext.data.ArrayStore({
                id: 0,
                fields: ['id', 'display'],
                data: [[10, '10'], [25, '25'], [50, '50'], [100, '100']]
            }),
            valueField: 'id',
            displayField: 'display',
            editable: false,
            value: GRID_DEFAULT_PAGE_SIZE,
            listeners: {
                select: function(combo, record, index) {
                    pagingBar.pageSize = record.id;
                    pagingBar.changePage(1);
                }
            }
        }
    ]
});

//************** GRID CONTEXT MENU ********************
var popupMenu = new Ext.menu.Menu({
    items: [
        { text: 'Edit',
            id: 'popupEdit',
            iconCls: 'edit',
            handler: function() {
                showWindow();
            }
        },
        { text: 'Delete',
            id: 'popupDelete',
            iconCls: 'del',
            disabled: false,
            handler: function() {
                var recsToDelete = new Array();
                recsToDelete[0] = ctxMenuRecord;
                doDeleteSelected(recsToDelete);
            }
        },
        { text: 'Delete Selected',
            id: 'popupDeleteSelected',
            iconCls: 'del',
            visible: false,
            disabled: false,
            handler: function() {
                var selected = gridUser.selModel.selections;
                var recsToDelete = new Array();
                for (var i = selected.length; i > 0; i--) {
                    recsToDelete[i - 1] = selected.items[i - 1];
                }
                doDeleteSelected(recsToDelete);
            }
        }
    ]
});

//This is a custom class, not part of ExtJS!!
//It is used in the edit-grid example at  
//http://extjs.com/deploy/dev/examples/grid/edit-grid.html
//See top of this code the prototype definition
//for a CheckColumn type.
var pswdChangeColumn = new Ext.grid.CheckColumn({
    header: 'Pswd Chg Req?',
    dataIndex: 'PasswordChangeRequired',
    width: 100
});

/*********** STATUS BAR *************/
var sb = new Ext.StatusBar({
    defaultText: '',
    id: 'topStatusBar',
    statusAlign: 'right',
    items:
    [
        {
            xtype: 'tbbutton',
            cls: 'x-btn-text-icon',
            icon: '/icons/add.png',
            text: 'Add New User',
            handler: function() {
                ctxMenuRecord = null;
                showWindow();
            }
        }, '-',
        {
            xtype: 'tbbutton',
            id: 'btnDelete',
            cls: 'x-btn-text-icon',
            icon: '/icons/delete.png',
            text: 'Delete Selected',
            handler: function() {
                var selected = gridUser.selModel.selections;
                var recsToDelete = new Array();
                for (var i = selected.length; i > 0; i--) {
                    recsToDelete[i - 1] = selected.items[i - 1];
                }
                doDeleteSelected(recsToDelete);
            }
        }, '-',
        {
            xtype: 'tbbutton',
            id: 'btnRefresh',
            cls: 'x-btn-text-icon',
            icon: '/icons/bullet_lightning.png',
            text: 'Refresh Data',
            handler: function() {
                gridUser.selModel.deselectRange(0, 999);
                dsUsers.load();
                SetStatus('Data refreshed', true);
            }
        }
    ]
});

//************** GRID ********************
var sm = new Ext.grid.CheckboxSelectionModel();
var gridUser = new Ext.grid.GridPanel({
    title: 'Users',
    region: 'center',
    //************** GRID TOP BUTTONS ********************
    tbar: sb,
    sm: new Ext.grid.CheckboxSelectionModel(),
    frame: false,
    store: dsUsers,
    height: 400,
    width: 750,
    //************** GRID COLUMNS ********************
    cm: new Ext.grid.ColumnModel([
        sm,
        { width: 50, hidden: false, sortable: true, header: "UserID", dataIndex: "UserID" },
        { width: 60, sortable: true, header: "First", dataIndex: "FirstName" },
        { width: 60, sortable: true, header: "Last", dataIndex: "LastName" },
        pswdChangeColumn,
        { width: 90, sortable: true, header: "UserName", dataIndex: "UserName" },
        { width: 150, sortable: true, header: "Email", dataIndex: "Email" },
        { width: 90, hidden: true, sortable: true, header: "Address1", dataIndex: "Address1" },
        { width: 90, hidden: true, sortable: true, header: "Address2", dataIndex: "Address2" },
        { width: 90, hidden: true, sortable: true, header: "City", dataIndex: "City" },
        { width: 30, hidden: true, sortable: true, header: "State", dataIndex: "State" },
        { width: 30, hidden: true, sortable: true, header: "City", dataIndex: "City" },
        { width: 90, sortable: true, header: "HomePhone", dataIndex: "HomePhone" },
        { width: 90, sortable: true, header: "MobilePhone", dataIndex: "MobilePhone" },
        { width: 90, hidden: true, type: "date", sortable: true, header: "PasswordSetDate", dataIndex: "PasswordSetDate" }
    ]),
    //********** BOTTOM PAGING TOOLBAR ***************
    bbar: pagingBar
});

//************** GRID RIGHT-CLICK MENU ********************
gridUser.on('rowcontextmenu', function(grid, rowIndex, e) {
    e.stopEvent(); // Stops the browser context menu from showing.
    ctxMenuRow = grid.getView().getRow(rowIndex);
    ctxMenuRecord = dsUsers.getAt(rowIndex);
    name = ctxMenuRecord.data.FirstName + ' ' + ctxMenuRecord.data.LastName;
    Ext.getCmp('popupEdit').setText("Edit '" + name + "'");
    Ext.getCmp('popupDelete').setText("Delete '" + name + "' only.");

    selectedCount = grid.selModel.selections.length;
    if (selectedCount >= 1) {
        Ext.getCmp('popupDeleteSelected').setText("Delete selected rows (" + selectedCount + " selected).");
    }
    popupMenu.items.items[2].hidden = (selectedCount < 1);
    popupMenu.showAt(e.getXY());
});

gridUser.getSelectionModel().on('selectionchange',
	function(selModel) {
	    SyncDelButton();
	},
	this,
	{ buffer: 10 }
);

//************** GRID DOUBLE-CLICK ********************
gridUser.on('rowdblclick', function(grid, rowIndex, e) {
    ctxMenuRow = grid.getView().getRow(rowIndex);
    ctxMenuRecord = dsUsers.getAt(rowIndex);
    showWindow();
});

//************** POPUP FORM PANEL ********************
var frmMain = new Ext.FormPanel({
    labelWidth: 100,
    url: '/rest/user',
    method: 'POST',
    monitorValid: true,
    frame: true,
    defaults: { width: 180, selectOnFocus: true },
    items: [
        { selectOnFocus: true, allowBlank: false, id: 'FirstName', xtype: 'textfield', name: 'FirstName', fieldLabel: "First", dataIndex: "FirstName", required: true },
        { allowBlank: false, xtype: 'textfield', name: 'LastName', fieldLabel: "Last", dataIndex: "LastName" },
        { allowBlank: false, xtype: 'textfield', name: 'UserName', fieldLabel: "UserName", dataIndex: "UserName" },
        { xtype: 'textfield', name: 'Email', fieldLabel: "Email", vtype: 'email', dataIndex: "Email" },
        { xtype: 'textfield', name: 'Address1', fieldLabel: "Address1", dataIndex: "Address1" },
        { xtype: 'textfield', name: 'Address2', fieldLabel: "Address2", dataIndex: "Address2" },
        { xtype: 'textfield', name: 'City', fieldLabel: "City", dataIndex: "City" },
        { xtype: 'combo', triggerAction: 'all', width: 50, mode: 'local', store: dsStates, displayField: 'abbr', name: 'State', fieldLabel: "State", dataIndex: "State" },
        { xtype: 'textfield', name: 'Zip', fieldLabel: "Zip", dataIndex: "Zip" },
        { xtype: 'textfield', name: 'HomePhone', fieldLabel: "HomePhone", dataIndex: "HomePhone" },
        { xtype: 'textfield', name: 'MobilePhone', fieldLabel: "MobilePhone", dataIndex: "MobilePhone" },
        { xtype: 'datefield', name: 'PasswordSetDate', fieldLabel: "PasswordSetDate", width: 100, dataIndex: "PasswordSetDate" },
        { xtype: 'checkbox', name: 'PasswordChangeRequired', fieldLabel: "PasswordChangeRequired", dataIndex: "PasswordChangeRequired" }
    ],
    buttons: [
    {
        //************** POPUP BUTTON "SAVE" ********************
        text: 'Save',
        cls: 'x-btn-text-icon',
        icon: '/icons/accept.png',
        formBind: true,
        minWidth: 75,
        handler: function() {
            adding = (ctxMenuRecord == null);
            f = frmMain.getForm();
            f.method = (adding ? 'POST' : 'PUT');
            f.url = (adding ? '/rest/user' : '/rest/user/' + ctxMenuRecord.data.UserID);
            f.submit(
                {
                    /*if the returned JSON has success: true then this will hit.
                    Otherwise, if success:false is found, the failure function will be called.*/
                    success: function(f, a) {
                        /*a is an "action" of type Ext.form.Action.Submit.
                        When a form is submitted the action (in this case its a "Submit" type)
                        takes care of sending to the server and processing the response.
                        The assumption is that the returned HTML is a JSON object so
                        in the return Ext.form.Action.Submit object (my "a" variable)
                        there's a "result" property that was built by taking the
                        returned HTML and using the javascript "eval" command to turn
                        that JSON into a javascript object.
                        */
                        ctxMenuRecord = window.ctxMenuRecord;
                        adding = (ctxMenuRecord == null);
                        if (adding) {
                            var rt = dsUsers.recordType;
                            var ctxMenuRecord = new rt();
                            ctxMenuRecord.loadDefaults();
                        }
                        for (var i in a.result.data) {
                            fldName = i;
                            fldValue = a.result.data[i];
                            ctxMenuRecord.set(fldName, fldValue)
                        }
                        if (adding) dsUsers.addSorted(ctxMenuRecord);
                        ctxMenuRecord.commit();

                        dsUsers.commitChanges();
                        winMain.hide()
                        msg = ctxMenuRecord.data.FirstName + ' ' + (adding ? 'added' : 'updated') + '!'
                        SetStatus(msg, true);
                        dsUsers.commitChanges();
                        //Ext.Msg.alert('Database Updates', msg);
                    },
                    failure: function(f, a) {
                        if (a.failureType === Ext.form.Action.CONNECT_FAILURE) {
                            Ext.Msg.alert('Failure', 'Server reported:' + a.response.status + ' ' + a.response.statusText);
                        }
                        if (a.failureType === Ext.form.Action.SERVER_INVALID) {
                            Ext.Msg.alert('Warning', a.result.errormsg);
                        }
                    }
                }
            );
        }
    },
    {
        //************** POPUP BUTTON "CANCEL" ********************
        text: 'Cancel',
        minWidth: 75,
        cls: 'x-btn-text-icon',
        icon: '/icons/cancel.png',
        handler: function() {
            winMain.hide()
        }
}]
    });

    frmMain.on('render', function() {
        var first = frmMain.items.items[0];
        first.focus('', 100);
    });

    //************** POPUP MODAL WINDOW ********************
    winMain = new Ext.Window({
        height: 440,
        width: 320,
        border: false,
        closable: true,
        closeAction: 'hide',
        modal: true,
        title: 'User Details',
        layout: 'fit',
        items: frmMain
    });

    //************** SHOW WINDOW ********************
    var showWindow = function() {
        //Critical! frmMain must be rendered first before you
        //can call its loadRecord method.  See my forum post at
        //http://extjs.com/forum/showthread.php?p=324259#post324259
        f = frmMain.getForm();
        if (ctxMenuRecord == null) {
            f.reset();
            f.baseParams = { UserID: 0 };
            f.url = "rest/user/"
            winMain.show();
        }
        else {
            id = ctxMenuRecord.get("UserID");
            f.baseParams = { UserID: id };
            f.url = "rest/user/" + id
            winMain.show();
            f.loadRecord(ctxMenuRecord);
        }
        var first = frmMain.items.items[0];
        first.focus('', 100);
    }

    //************** PROCESS DELETES ********************
    var doDeleteSelected = function(recs) {
        var delCount = recs.length;

        if (delCount == 1) {
            rec = recs[0];
            name = rec.data.FirstName + ' ' + rec.data.LastName;
            question = "'Are you sure you want to delete '" + name + "'?";
        }
        else {
            question = 'Are you sure you want to delete all ' + delCount + ' selected users?';
        }
        Ext.Msg.show({
            title: 'Confirm Deletion',
            msg: question,
            buttons: {
                yes: true,
                no: false,
                cancel: true
            },
            icon: 'images/warning.jpg',
            fn: function(btn) {
                if (btn == 'yes') {
                    //**Ajax Updates
                    ids = "";
                    for (var i = 0; i < recs.length; i++) {
                        if (i > 0) ids += ",";
                        ids += recs[i].data.UserID;
                    }

                    var sb = Ext.getCmp('topStatusBar');
                    sb.showBusy('Deleting records...');

                    /****** RUN AJAX DELETE *******/
                    Ext.Ajax.request({
                        url: 'rest/user',
                        method: 'DELETE',
                        params: { ids: ids },
                        success: function(result, request) {
                            return;
                            // Convert the json to an object
                            var resultData = Ext.util.JSON.decode(result.responseText);
                            // Check for "logic" errors. ie the transport part of the request worked 
                            // but there was some logical reason your server-side script disallowed the action
                            if (!resultData.success) {
                                Ext.MessageBox.alert('Error', 'There was an error: ' + resultData.msg);
                            }
                            else Ext.MessageBox.alert('Success', 'All was well!');
                        },
                        failure: function(result, request) {
                            alert("failure!");
                            return;
                            // Check for "transport" errors eg page not found, page crashed
                            // on server side, etc
                            Ext.MessageBox.alert('Error', 'There was an error: ' + result.statusText);
                        }
                    });

                    var len = recs.length;
                    for (var i = len; i > 0; i--) {
                        a = recs[i - 1];
                        dsUsers.remove(recs[i - 1]);
                    }
                    SetStatus(len + ' record' + (len == 1 ? '' : 's') + ' deleted!', true);
                }
            }
        });
    }

    var SyncDelButton = function() {
        if (gridUser.selModel.selections.length == 0)
            Ext.getCmp('btnDelete').disable();
        else
            Ext.getCmp('btnDelete').enable();
    }

    var SetStatus = function(msg, successful) {
        var sb = Ext.getCmp('topStatusBar');

        if (msg == "")
            sb.clearStatus({ anim: true });
        else {
            if (successful)
                sb.setStatus({ iconCls: 'x-status-success', text: msg });
            else
                sb.setStatus({ iconCls: 'x-status-fail', text: msg });
        }
    }

    /******* GRID MASK ***********/
    //Note: see the dsUsers loadexception event above.
    //it relies on this mask object to hide it in the
    //case that the store has problems while loading.

    //var mask = new Ext.LoadMask(grid.el, { msg: 'Loading data...', store: dsUsers });


    Ext.onReady(function() {
        dsUsers.load({ params: { start: 0, limit: 10} });
        SyncDelButton();
    });

    this.Sleep = function zZzZzZ(naptime) {
        naptime = naptime * 1000;
        var sleeping = true;
        var now = new Date();
        var alarm;
        var startingMSeconds = now.getTime();
        while (sleeping) {
            alarm = new Date();
            alarmMSeconds = alarm.getTime();
            if (alarmMSeconds - startingMSeconds > naptime) { sleeping = false; }
        }
    }

//************** SECURITY TAB *****************
var tabUserGroup = new Ext.TabPanel({
    region: 'center',
    activeTab: 0,
    //plain=true sets the tab background to use css x-tab-panel-header-plain
    //which is caused the underlying color to come out.  Notice in the master
    //viewport below I've had to set the style to background=white.
    plain: true,
    defaults: { autoScroll: true },
    items: [
            gridUser,
            {
                title: 'Groups'
            }
        ]
});

//***** MAIN APP SECURITY TAB ******//
var tabSecurity = {
    title: 'Security',
    layout: 'border',
    iconCls: 'securityTab',
    items:
        [
            tabUserGroup
        ]
};